package com.googlecode.bluetools.common.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 
 */
public class FileUtil {

	/**
	 * list Files recursively
	 * 
	 * @param filter
	 * @return File list
	 * @deprecated use {@link org.apache.commons.io.FileUtils}
	 * @see {@link org.apache.commons.io.FileUtils#iterateFiles(File, org.apache.commons.io.filefilter.IOFileFilter, org.apache.commons.io.filefilter.IOFileFilter)}
	 * @see {@link org.apache.commons.io.FileUtils#listFiles(File, org.apache.commons.io.filefilter.IOFileFilter, org.apache.commons.io.filefilter.IOFileFilter)}
	 */
	public static List listFiles(File dir, FileFilter filter) {
		List files = new ArrayList();
		if (!dir.exists() || dir.isFile())
			return files;
		listFiles(files, dir, filter);
		return files;
	}

	private static void listFiles(List filesList, File dir, FileFilter filter) {
		File[] files = dir.listFiles(filter);
		List temp = Arrays.asList(files);
		Collections.sort(temp);
		filesList.addAll(temp);

		File[] subDirs = dir.listFiles(FileFilters.directoryFileFilter());
		for (int i = 0; i < subDirs.length; i++) {
			listFiles(filesList, subDirs[i], filter);
		}
	}

	/**
	 * Returns a path that uses '/' as the path separator and that doesn't end with a path separator.
	 * 
	 * @param path
	 * @return String
	 */
	public static String normalizePath(String path) {
		if (path == null)
			return null;

		path = path.trim().replace('\\', '/');
		if (path.endsWith("/") && (!path.endsWith("//")))
			path = path.substring(0, (path.length() - "/".length()));

		return path;
	}

	/**
	 * Returns a path that uses '/' as the path separator and that doesn't end with a path separator.
	 * 
	 * @param path
	 * @return String
	 */
	public static String normalizePath(File path) {
		return normalizePath(path.getAbsolutePath());
	}

	/**
	 * Returns a normalized path that is the result of appending path2 to path1.
	 * 
	 * @param path1
	 * @param path2
	 * @return String
	 */
	public static String appendPath(String path1, String path2) {
		path1 = normalizePath(path1);
		path2 = normalizePath(path2);

		if ((path1 == null) || (path1.trim().length() == 0))
			return path2;

		if ((path2 == null) || (path2.trim().length() == 0))
			return path1;

		if (path1.endsWith("//")) {
			if (path2.startsWith("/") && (path2.length() > 1))
				path2 = path2.substring(1);
		}
		else if (!path2.startsWith("/"))
			path2 = "/" + path2;

		return path1 + path2;
	}

	/**
	 * Returns a normalized path that doesn't contain the last segment from the specified path. The last segment is any
	 * text at the right of the last file separator.
	 * 
	 * @param path
	 * @return String
	 */
	public static String removeLastSegment(String path) {
		path = normalizePath(path);

		int index = path.lastIndexOf("/");
		if (index < 0)
			return "";

		return path.substring(0, index);
	}

	/**
	 * Returns the last segment of a path. The last segment is any text of the right of the last path separator (\ or
	 * /).
	 * 
	 * The returned string never starts or ends with a path separator.
	 * 
	 * @param path
	 * @return String
	 */
	public static String getLastSegment(String path) {
		path = normalizePath(path);

		int index = path.lastIndexOf("/");
		if (index >= 0)
			path = path.substring(index + 1);

		return path;
	}

	/**
	 * Returns a file handle that is an absolute path to an existing file or directory.
	 * 
	 * @param file
	 * @return File
	 */
	public static File getValidFileOrDirectory(File file) {
		if ((file == null) || (!file.exists()))
			return null;

		try {
			return file.getAbsoluteFile().getCanonicalFile();
		}
		catch (IOException ioE) {
		}

		return null;
	}

	/**
	 * Returns the extension of a file. The extension is any text at the right side of the last '.'.
	 * 
	 * <p>
	 * If the file has no extension or is null or is a directory this method returns an empty string.
	 * 
	 * @param file
	 * @return String
	 */
	public static String getFileExtension(File file) {
		if ((file == null) || file.isDirectory())
			return "";

		return getFileExtension(file.getName());
	}

	/**
	 * Returns the extension of a given file full path or name. The extension is any text at the right side of the last
	 * '.'.
	 * 
	 * <p>
	 * If the file has no extension or is null returns an empty string.
	 * 
	 * @param file
	 * @return String
	 */
	public static String getFileExtension(String file) {
		if (file == null) {
			throw new IllegalArgumentException("null arg: file");
		}

		int dotIdx = file.lastIndexOf('.');
		String ext = "";
		if (dotIdx > 0 && dotIdx != (file.length() - 1)) {
			ext = file.substring(dotIdx + 1);
		}

		return ext;
	}

	/**
	 * Extract the filename from the given path, e.g. "mypath/myfile.txt" -> "myfile.txt".
	 * 
	 * @param file
	 * @return
	 */
	public static String getFileName(String file) {
		if (file == null)
			return "";
		int sIdx = file.lastIndexOf('/');
		int bsIdx = file.lastIndexOf('\\');
		int idx = Math.max(sIdx, bsIdx);

		return file.substring(idx + 1);
	}

	/**
	 * Returns true if the file to analyze is in the parent candidate's hierarchy.
	 * 
	 * @param parentCandidate
	 * @param fileToAnalyse
	 * @return boolean
	 */
	public static boolean isParent(File parentCandidate, File fileToAnalyse) {
		if ((fileToAnalyse == null) || (parentCandidate == null))
			return false;

		parentCandidate = parentCandidate.getAbsoluteFile();
		fileToAnalyse = fileToAnalyse.getAbsoluteFile();

		while (fileToAnalyse != null) {
			if (parentCandidate.equals(fileToAnalyse))
				return true;

			fileToAnalyse = fileToAnalyse.getParentFile();
		}

		return false;
	}

	/**
	 * Returns the system temp directory.
	 * 
	 * @return File
	 * @throws IOException
	 */
	public static File getTempDir() {
		return new File(System.getProperty("java.io.tmpdir"));
	}

	public static boolean exist(String filePath) {
		return (new File(filePath)).exists();
	}

	/**
	 * @return the contents of the file in a byte array.
	 */
	public static byte[] getBytesFromFile(File file) throws IOException {
		InputStream is = new FileInputStream(file);
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		int maxLen = 4096;
		byte[] bytes = new byte[maxLen];
		int nBytes = 0;
		while ((nBytes = is.read(bytes, 0, 4096)) > 0) {
			bos.write(bytes, 0, nBytes);
		}
		// Close the input stream and return bytes
		is.close();

		return bos.toByteArray();
	}

	/**
	 * break a path down into individual elements and add to a list. example : if a path is /a/b/c/d.txt, the breakdown
	 * will be [d.txt,c,b,a]
	 * 
	 * @param f input file
	 * @return a List collection with the individual elements of the path in reverse order
	 */
	private static List<String> getPathList(File f) {
		List<String> l = new ArrayList<String>();
		File r;
		try {
			r = f.getCanonicalFile();
			while (r != null) {
				l.add(r.getName());
				r = r.getParentFile();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			l = null;
		}
		return l;
	}

	/**
	 * figure out a string representing the relative path of 'f' with respect to 'r'
	 * 
	 * @param r home path
	 * @param f path of file
	 */
	private static String matchPathLists(List<String> r, List<String> f) {
		int i;
		int j;
		String s;
		// start at the beginning of the lists
		// iterate while both lists are equal
		s = "";
		i = r.size() - 1;
		j = f.size() - 1;

		// first eliminate common root
		while ((i >= 0) && (j >= 0) && (r.get(i).equals(f.get(j)))) {
			i--;
			j--;
		}

		// for each remaining level in the home path, add a ..
		for (; i >= 0; i--) {
			s += ".." + File.separator;
		}

		// for each level in the file path, add the path
		for (; j >= 1; j--) {
			s += f.get(j) + File.separator;
		}

		// file name
		s += f.get(j);
		return s;
	}

	/**
	 * get relative path of File 'f' with respect to 'home' directory example : home = /a/b/c f = /a/d/e/x.txt s =
	 * getRelativePath(home,f) = ../../d/e/x.txt
	 * 
	 * @param home base path, should be a directory, not a file, or it doesn't make sense
	 * @param f file to generate path for
	 * @return path from home to f as a string
	 */
	public static String getRelativePath(File home, File f) {
		List<String> homelist;
		List<String> filelist;
		String s;

		homelist = getPathList(home);
		filelist = getPathList(f);
		s = matchPathLists(homelist, filelist);

		return s;
	}
}
